import {
  AnimationClip,
  AnimationMixer,
  Bone,
  BufferGeometry,
  FileLoader,
  Float32BufferAttribute,
  FrontSide,
  Loader,
  LoaderUtils,
  Matrix4,
  Mesh,
  MeshPhongMaterial,
  Quaternion,
  Skeleton,
  SkinnedMesh,
  TextureLoader,
  Uint16BufferAttribute,
  Vector2,
  Vector3,
} from 'three'
import { decodeText } from '../_polyfill/LoaderUtils'

var XLoader = /* @__PURE__ */ (function () {
  var classCallCheck = function (instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError('Cannot call a class as a function')
    }
  }

  var createClass = (function () {
    function defineProperties(target, props) {
      for (let i = 0; i < props.length; i++) {
        var descriptor = props[i]
        descriptor.enumerable = descriptor.enumerable || false
        descriptor.configurable = true
        if ('value' in descriptor) descriptor.writable = true
        Object.defineProperty(target, descriptor.key, descriptor)
      }
    }

    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps)
      if (staticProps) defineProperties(Constructor, staticProps)
      return Constructor
    }
  })()

  var XboneInf = function XboneInf() {
    classCallCheck(this, XboneInf)

    this.boneName = ''
    this.BoneIndex = 0
    this.Indeces = []
    this.Weights = []
    this.initMatrix = null
    this.OffsetMatrix = null
  }

  var XAnimationInfo = function XAnimationInfo() {
    classCallCheck(this, XAnimationInfo)

    this.animeName = ''
    this.boneName = ''
    this.targetBone = null
    this.keyType = 4
    this.frameStartLv = 0
    this.keyFrames = []
    this.InverseMx = null
  }

  var XAnimationObj = (function () {
    function XAnimationObj(_flags) {
      classCallCheck(this, XAnimationObj)

      this.fps = 30
      this.name = 'xanimation'
      this.length = 0
      this.hierarchy = []
      this.putFlags = _flags
      if (this.putFlags.putPos === undefined) {
        this.putFlags.putPos = true
      }

      if (this.putFlags.putRot === undefined) {
        this.putFlags.putRot = true
      }

      if (this.putFlags.putScl === undefined) {
        this.putFlags.putScl = true
      }
    }

    createClass(XAnimationObj, [
      {
        key: 'make',
        value: function make(XAnimationInfoArray) {
          for (let i = 0; i < XAnimationInfoArray.length; i++) {
            this.hierarchy.push(this.makeBonekeys(XAnimationInfoArray[i]))
          }

          this.length = this.hierarchy[0].keys[this.hierarchy[0].keys.length - 1].time
        },
      },
      {
        key: 'clone',
        value: function clone() {
          return Object.assign({}, this)
        },
      },
      {
        key: 'makeBonekeys',
        value: function makeBonekeys(XAnimationInfo) {
          var refObj = {}
          refObj.name = XAnimationInfo.boneName
          refObj.parent = ''
          refObj.keys = this.keyFrameRefactor(XAnimationInfo)
          refObj.copy = function () {
            return Object.assign({}, this)
          }

          return refObj
        },
      },
      {
        key: 'keyFrameRefactor',
        value: function keyFrameRefactor(XAnimationInfo) {
          var keys = []
          for (let i = 0; i < XAnimationInfo.keyFrames.length; i++) {
            var keyframe = {}
            keyframe.time = XAnimationInfo.keyFrames[i].time * this.fps
            if (XAnimationInfo.keyFrames[i].pos && this.putFlags.putPos) {
              keyframe.pos = XAnimationInfo.keyFrames[i].pos
            }

            if (XAnimationInfo.keyFrames[i].rot && this.putFlags.putRot) {
              keyframe.rot = XAnimationInfo.keyFrames[i].rot
            }

            if (XAnimationInfo.keyFrames[i].scl && this.putFlags.putScl) {
              keyframe.scl = XAnimationInfo.keyFrames[i].scl
            }

            if (XAnimationInfo.keyFrames[i].matrix) {
              keyframe.matrix = XAnimationInfo.keyFrames[i].matrix
              if (this.putFlags.putPos) {
                keyframe.pos = new Vector3().setFromMatrixPosition(keyframe.matrix)
              }

              if (this.putFlags.putRot) {
                keyframe.rot = new Quaternion().setFromRotationMatrix(keyframe.matrix)
              }

              if (this.putFlags.putScl) {
                keyframe.scl = new Vector3().setFromMatrixScale(keyframe.matrix)
              }
            }

            keys.push(keyframe)
          }

          return keys
        },
      },
    ])
    return XAnimationObj
  })()

  var XKeyFrameInfo = function XKeyFrameInfo() {
    classCallCheck(this, XKeyFrameInfo)

    this.index = 0
    this.Frame = 0
    this.time = 0.0
    this.matrix = null
  }

  var XLoader = (function () {
    function XLoader(manager) {
      Loader.call(this, manager)

      classCallCheck(this, XLoader)

      this.debug = false
      this.texloader = new TextureLoader(this.manager)
      this.url = ''
      this._putMatLength = 0
      this._nowMat = null
      this._nowFrameName = ''
      this.frameHierarchie = []
      this.Hierarchies = {}
      this.HieStack = []
      this._currentObject = {}
      this._currentFrame = {}
      this._data = null
      this.onLoad = null
      this.IsUvYReverse = true
      this.Meshes = []
      this.animations = []
      this.animTicksPerSecond = 30
      this._currentGeo = null
      this._currentAnime = null
      this._currentAnimeFrames = null
    }

    createClass(XLoader, [
      {
        key: '_setArgOption',
        value: function _setArgOption(_arg) {
          var _start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0

          if (!_arg) {
            return
          }

          for (let i = _start; i < _arg.length; i++) {
            switch (i) {
              case 0:
                this.url = _arg[i]
                break
              case 1:
                this.options = _arg[i]
                break
            }
          }

          if (this.options === undefined) {
            this.options = {}
          }
        },
      },
      {
        key: 'load',
        value: function load(_arg, onLoad, onProgress, onError) {
          var _this = this

          this._setArgOption(_arg)
          var loader = new FileLoader(this.manager)
          loader.setPath(this.path)
          loader.setResponseType('arraybuffer')
          loader.setRequestHeader(this.requestHeader)
          loader.setWithCredentials(this.withCredentials)
          loader.load(
            this.url,
            function (response) {
              try {
                _this.parse(response, onLoad)
              } catch (e) {
                if (onError) {
                  onError(e)
                } else {
                  console.error(e)
                }

                _this.manager.itemError(_this.url)
              }
            },
            onProgress,
            onError,
          )
        },
      },
      {
        key: '_readLine',
        value: function _readLine(line) {
          var readed = 0
          while (true) {
            var find = -1
            find = line.indexOf('//', readed)
            if (find === -1) {
              find = line.indexOf('#', readed)
            }

            if (find > -1 && find < 2) {
              var foundNewLine = -1
              foundNewLine = line.indexOf('\r\n', readed)
              if (foundNewLine > 0) {
                readed = foundNewLine + 2
              } else {
                foundNewLine = line.indexOf('\r', readed)
                if (foundNewLine > 0) {
                  readed = foundNewLine + 1
                } else {
                  readed = line.indexOf('\n', readed) + 1
                }
              }
            } else {
              break
            }
          }

          return line.substr(readed)
        },
      },
      {
        key: '_readLine',
        value: function _readLine(line) {
          var readed = 0
          while (true) {
            var find = -1
            find = line.indexOf('//', readed)
            if (find === -1) {
              find = line.indexOf('#', readed)
            }

            if (find > -1 && find < 2) {
              var foundNewLine = -1
              foundNewLine = line.indexOf('\r\n', readed)
              if (foundNewLine > 0) {
                readed = foundNewLine + 2
              } else {
                foundNewLine = line.indexOf('\r', readed)
                if (foundNewLine > 0) {
                  readed = foundNewLine + 1
                } else {
                  readed = line.indexOf('\n', readed) + 1
                }
              }
            } else {
              break
            }
          }

          return line.substr(readed)
        },
      },
      {
        key: '_isBinary',
        value: function _isBinary(binData) {
          var reader = new DataView(binData)
          var face_size = (32 / 8) * 3 + (32 / 8) * 3 * 3 + 16 / 8
          var n_faces = reader.getUint32(80, true)
          var expect = 80 + 32 / 8 + n_faces * face_size
          if (expect === reader.byteLength) {
            return true
          }

          var fileLength = reader.byteLength
          for (let index = 0; index < fileLength; index++) {
            if (reader.getUint8(index, false) > 127) {
              return true
            }
          }

          return false
        },
      },
      {
        key: '_ensureBinary',
        value: function _ensureBinary(buf) {
          if (typeof buf === 'string') {
            var array_buffer = new Uint8Array(buf.length)
            for (let i = 0; i < buf.length; i++) {
              array_buffer[i] = buf.charCodeAt(i) & 0xff
            }

            return array_buffer.buffer || array_buffer
          } else {
            return buf
          }
        },
      },
      {
        key: '_ensureString',
        value: function _ensureString(buf) {
          if (typeof buf !== 'string') {
            return decodeText(new Uint8Array(buf))
          } else {
            return buf
          }
        },
      },
      {
        key: 'parse',
        value: function _parse(data, onLoad) {
          var binData = this._ensureBinary(data)
          this._data = this._ensureString(data)
          this.onLoad = onLoad
          return this._isBinary(binData) ? this._parseBinary(binData) : this._parseASCII()
        },
      },
      {
        key: '_parseBinary',
        value: function _parseBinary(data) {
          return this._parseASCII(decodeText(new Uint8Array(data)))
        },
      },
      {
        key: '_parseASCII',
        value: function _parseASCII() {
          var path

          if (this.resourcePath !== '') {
            path = this.resourcePath
          } else if (this.path !== '') {
            path = this.path
          } else {
            path = LoaderUtils.extractUrlBase(this.url)
          }

          this.texloader.setPath(path).setCrossOrigin(this.crossOrigin)

          var endRead = 16
          this.Hierarchies.children = []
          this._hierarchieParse(this.Hierarchies, endRead)
          this._changeRoot()
          this._currentObject = this.Hierarchies.children.shift()
          this._mainloop()
        },
      },
      {
        key: '_hierarchieParse',
        value: function _hierarchieParse(_parent, _end) {
          var endRead = _end
          while (true) {
            var find1 = this._data.indexOf('{', endRead) + 1
            var findEnd = this._data.indexOf('}', endRead)
            var findNext = this._data.indexOf('{', find1) + 1
            if (find1 > 0 && findEnd > find1) {
              var _currentObject = {}
              _currentObject.children = []
              var nameData = this._readLine(this._data.substr(endRead, find1 - endRead - 1)).trim()
              var word = nameData.split(/ /g)
              if (word.length > 0) {
                _currentObject.type = word[0]
                if (word.length >= 2) {
                  _currentObject.name = word[1]
                } else {
                  _currentObject.name = word[0] + this.Hierarchies.children.length
                }
              } else {
                _currentObject.name = nameData
                _currentObject.type = ''
              }

              if (_currentObject.type === 'Animation') {
                _currentObject.data = this._data.substr(findNext, findEnd - findNext).trim()
                var refs = this._hierarchieParse(_currentObject, findEnd + 1)
                endRead = refs.end
                _currentObject.children = refs.parent.children
              } else {
                var DataEnder = this._data.lastIndexOf(';', findNext > 0 ? Math.min(findNext, findEnd) : findEnd)
                _currentObject.data = this._data.substr(find1, DataEnder - find1).trim()
                if (findNext <= 0 || findEnd < findNext) {
                  endRead = findEnd + 1
                } else {
                  var nextStart = Math.max(DataEnder + 1, find1)
                  var _refs = this._hierarchieParse(_currentObject, nextStart)
                  endRead = _refs.end
                  _currentObject.children = _refs.parent.children
                }
              }

              _currentObject.parent = _parent
              if (_currentObject.type != 'template') {
                _parent.children.push(_currentObject)
              }
            } else {
              endRead = find1 === -1 ? this._data.length : findEnd + 1
              break
            }
          }

          return {
            parent: _parent,
            end: endRead,
          }
        },
      },
      {
        key: '_mainloop',
        value: function _mainloop() {
          var _this2 = this

          this._mainProc()
          if (this._currentObject.parent || this._currentObject.children.length > 0 || !this._currentObject.worked) {
            setTimeout(function () {
              _this2._mainloop()
            }, 1)
          } else {
            setTimeout(function () {
              _this2.onLoad({
                models: _this2.Meshes,
                animations: _this2.animations,
              })
            }, 1)
          }
        },
      },
      {
        key: '_mainProc',
        value: function _mainProc() {
          var breakFlag = false
          while (true) {
            if (!this._currentObject.worked) {
              switch (this._currentObject.type) {
                case 'template':
                  break
                case 'AnimTicksPerSecond':
                  this.animTicksPerSecond = parseInt(this._currentObject.data)
                  break
                case 'Frame':
                  this._setFrame()
                  break
                case 'FrameTransformMatrix':
                  this._setFrameTransformMatrix()
                  break
                case 'Mesh':
                  this._changeRoot()
                  this._currentGeo = {}
                  this._currentGeo.name = this._currentObject.name.trim()
                  this._currentGeo.parentName = this._getParentName(this._currentObject).trim()
                  this._currentGeo.VertexSetedBoneCount = []
                  this._currentGeo.GeometryData = {
                    vertices: [],
                    normals: [],
                    uvs: [],
                    skinIndices: [],
                    skinWeights: [],
                    indices: [],
                    materialIndices: [],
                  }
                  this._currentGeo.Materials = []
                  this._currentGeo.normalVectors = []
                  this._currentGeo.BoneInfs = []
                  this._currentGeo.baseFrame = this._currentFrame
                  this._makeBoneFrom_CurrentFrame()
                  this._readVertexDatas()
                  breakFlag = true
                  break
                case 'MeshNormals':
                  this._readVertexDatas()
                  break
                case 'MeshTextureCoords':
                  this._setMeshTextureCoords()
                  break
                case 'VertexDuplicationIndices':
                  break
                case 'MeshMaterialList':
                  this._setMeshMaterialList()
                  break
                case 'Material':
                  this._setMaterial()
                  break
                case 'SkinWeights':
                  this._setSkinWeights()
                  break
                case 'AnimationSet':
                  this._changeRoot()
                  this._currentAnime = {}
                  this._currentAnime.name = this._currentObject.name.trim()
                  this._currentAnime.AnimeFrames = []
                  break
                case 'Animation':
                  if (this._currentAnimeFrames) {
                    this._currentAnime.AnimeFrames.push(this._currentAnimeFrames)
                  }

                  this._currentAnimeFrames = new XAnimationInfo()
                  this._currentAnimeFrames.boneName = this._currentObject.data.trim()
                  break
                case 'AnimationKey':
                  this._readAnimationKey()
                  breakFlag = true
                  break
              }

              this._currentObject.worked = true
            }

            if (this._currentObject.children.length > 0) {
              this._currentObject = this._currentObject.children.shift()
              if (this.debug) {
                console.log('processing ' + this._currentObject.name)
              }

              if (breakFlag) break
            } else {
              if (this._currentObject.worked) {
                if (this._currentObject.parent && !this._currentObject.parent.parent) {
                  this._changeRoot()
                }
              }

              if (this._currentObject.parent) {
                this._currentObject = this._currentObject.parent
              } else {
                breakFlag = true
              }

              if (breakFlag) break
            }
          }

          return
        },
      },
      {
        key: '_changeRoot',
        value: function _changeRoot() {
          if (this._currentGeo != null && this._currentGeo.name) {
            this._makeOutputGeometry()
          }

          this._currentGeo = {}
          if (this._currentAnime != null && this._currentAnime.name) {
            if (this._currentAnimeFrames) {
              this._currentAnime.AnimeFrames.push(this._currentAnimeFrames)
              this._currentAnimeFrames = null
            }

            this._makeOutputAnimation()
          }

          this._currentAnime = {}
        },
      },
      {
        key: '_getParentName',
        value: function _getParentName(_obj) {
          if (_obj.parent) {
            if (_obj.parent.name) {
              return _obj.parent.name
            } else {
              return this._getParentName(_obj.parent)
            }
          } else {
            return ''
          }
        },
      },
      {
        key: '_setFrame',
        value: function _setFrame() {
          this._nowFrameName = this._currentObject.name.trim()
          this._currentFrame = {}
          this._currentFrame.name = this._nowFrameName
          this._currentFrame.children = []
          if (this._currentObject.parent && this._currentObject.parent.name) {
            this._currentFrame.parentName = this._currentObject.parent.name
          }

          this.frameHierarchie.push(this._nowFrameName)
          this.HieStack[this._nowFrameName] = this._currentFrame
        },
      },
      {
        key: '_setFrameTransformMatrix',
        value: function _setFrameTransformMatrix() {
          this._currentFrame.FrameTransformMatrix = new Matrix4()
          var data = this._currentObject.data.split(',')
          this._ParseMatrixData(this._currentFrame.FrameTransformMatrix, data)
          this._makeBoneFrom_CurrentFrame()
        },
      },
      {
        key: '_makeBoneFrom_CurrentFrame',
        value: function _makeBoneFrom_CurrentFrame() {
          if (!this._currentFrame.FrameTransformMatrix) {
            return
          }

          var b = new Bone()
          b.name = this._currentFrame.name
          b.applyMatrix4(this._currentFrame.FrameTransformMatrix)
          b.matrixWorld = b.matrix
          b.FrameTransformMatrix = this._currentFrame.FrameTransformMatrix
          this._currentFrame.putBone = b
          if (this._currentFrame.parentName) {
            for (let frame in this.HieStack) {
              if (this.HieStack[frame].name === this._currentFrame.parentName) {
                this.HieStack[frame].putBone.add(this._currentFrame.putBone)
              }
            }
          }
        },
      },
      {
        key: '_readVertexDatas',
        value: function _readVertexDatas() {
          var endRead = 0
          var mode = 0
          var mode_local = 0
          var maxLength = 0
          while (true) {
            var changeMode = false
            if (mode_local === 0) {
              var refO = this._readInt1(endRead)
              endRead = refO.endRead
              mode_local = 1
              maxLength = this._currentObject.data.indexOf(';;', endRead) + 1
              if (maxLength <= 0) {
                maxLength = this._currentObject.data.length
              }
            } else {
              var find = 0
              switch (mode) {
                case 0:
                  find = this._currentObject.data.indexOf(',', endRead) + 1
                  break
                case 1:
                  find = this._currentObject.data.indexOf(';,', endRead) + 1
                  break
              }

              if (find === 0 || find > maxLength) {
                find = maxLength
                mode_local = 0
                changeMode = true
              }

              switch (this._currentObject.type) {
                case 'Mesh':
                  switch (mode) {
                    case 0:
                      this._readVertex1(this._currentObject.data.substr(endRead, find - endRead))
                      break
                    case 1:
                      this._readFace1(this._currentObject.data.substr(endRead, find - endRead))
                      break
                  }

                  break
                case 'MeshNormals':
                  switch (mode) {
                    case 0:
                      this._readNormalVector1(this._currentObject.data.substr(endRead, find - endRead))
                      break
                  }

                  break
              }

              endRead = find + 1
              if (changeMode) {
                mode++
              }
            }

            if (endRead >= this._currentObject.data.length) {
              break
            }
          }
        },
      },
      {
        key: '_readInt1',
        value: function _readInt1(start) {
          var find = this._currentObject.data.indexOf(';', start)
          return {
            refI: parseInt(this._currentObject.data.substr(start, find - start)),
            endRead: find + 1,
          }
        },
      },
      {
        key: '_readVertex1',
        value: function _readVertex1(line) {
          var data = this._readLine(line.trim())
            .substr(0, line.length - 2)
            .split(';')
          this._currentGeo.GeometryData.vertices.push(parseFloat(data[0]), parseFloat(data[1]), parseFloat(data[2]))
          this._currentGeo.GeometryData.skinIndices.push(0, 0, 0, 0)
          this._currentGeo.GeometryData.skinWeights.push(1, 0, 0, 0)
          this._currentGeo.VertexSetedBoneCount.push(0)
        },
      },
      {
        key: '_readFace1',
        value: function _readFace1(line) {
          var data = this._readLine(line.trim())
            .substr(2, line.length - 4)
            .split(',')
          this._currentGeo.GeometryData.indices.push(
            parseInt(data[0], 10),
            parseInt(data[1], 10),
            parseInt(data[2], 10),
          )
        },
      },
      {
        key: '_readNormalVector1',
        value: function _readNormalVector1(line) {
          var data = this._readLine(line.trim())
            .substr(0, line.length - 2)
            .split(';')
          this._currentGeo.GeometryData.normals.push(parseFloat(data[0]), parseFloat(data[1]), parseFloat(data[2]))
        },
      },
      {
        key: '_buildGeometry',
        value: function _buildGeometry() {
          var bufferGeometry = new BufferGeometry()
          var position = []
          var normals = []
          var uvs = []
          var skinIndices = []
          var skinWeights = []

          //

          var data = this._currentGeo.GeometryData

          for (let i = 0, l = data.indices.length; i < l; i++) {
            var stride2 = data.indices[i] * 2
            var stride3 = data.indices[i] * 3
            var stride4 = data.indices[i] * 4

            position.push(data.vertices[stride3], data.vertices[stride3 + 1], data.vertices[stride3 + 2])
            normals.push(data.normals[stride3], data.normals[stride3 + 1], data.normals[stride3 + 2])
            skinIndices.push(
              data.skinIndices[stride4],
              data.skinIndices[stride4 + 1],
              data.skinIndices[stride4 + 2],
              data.skinIndices[stride4 + 3],
            )
            skinWeights.push(
              data.skinWeights[stride4],
              data.skinWeights[stride4 + 1],
              data.skinWeights[stride4 + 2],
              data.skinWeights[stride4 + 3],
            )
            uvs.push(data.uvs[stride2], data.uvs[stride2 + 1])
          }

          //

          bufferGeometry.setAttribute('position', new Float32BufferAttribute(position, 3))
          bufferGeometry.setAttribute('normal', new Float32BufferAttribute(normals, 3))
          bufferGeometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2))
          bufferGeometry.setAttribute('skinIndex', new Uint16BufferAttribute(skinIndices, 4))
          bufferGeometry.setAttribute('skinWeight', new Float32BufferAttribute(skinWeights, 4))

          this._computeGroups(bufferGeometry, data.materialIndices)

          return bufferGeometry
        },
      },
      {
        key: '_computeGroups',
        value: function _computeGroups(bufferGeometry, materialIndices) {
          var group
          var groups = []
          var materialIndex = undefined

          for (let i = 0; i < materialIndices.length; i++) {
            var currentMaterialIndex = materialIndices[i]

            if (currentMaterialIndex !== materialIndex) {
              materialIndex = currentMaterialIndex

              if (group !== undefined) {
                group.count = i * 3 - group.start
                groups.push(group)
              }

              group = {
                start: i * 3,
                materialIndex: materialIndex,
              }
            }
          }

          if (group !== undefined) {
            group.count = i * 3 - group.start
            groups.push(group)
          }

          bufferGeometry.groups = groups
        },
      },
      {
        key: '_setMeshTextureCoords',
        value: function _setMeshTextureCoords() {
          var endRead = 0
          var mode = 0
          var mode_local = 0
          while (true) {
            switch (mode) {
              case 0:
                if (mode_local === 0) {
                  var refO = this._readInt1(0)
                  endRead = refO.endRead
                  mode_local = 1
                } else {
                  var find = this._currentObject.data.indexOf(',', endRead) + 1
                  if (find === 0) {
                    find = this._currentObject.data.length
                    mode = 2
                    mode_local = 0
                  }

                  var line = this._currentObject.data.substr(endRead, find - endRead)
                  var data = this._readLine(line.trim()).split(';')
                  if (this.IsUvYReverse) {
                    this._currentGeo.GeometryData.uvs.push(parseFloat(data[0]), 1 - parseFloat(data[1]))
                  } else {
                    this._currentGeo.GeometryData.uvs.push(parseFloat(data[0]), parseFloat(data[1]))
                  }

                  endRead = find + 1
                }

                break
            }

            if (endRead >= this._currentObject.data.length) {
              break
            }
          }
        },
      },
      {
        key: '_setMeshMaterialList',
        value: function _setMeshMaterialList() {
          var endRead = 0
          var mode = 0
          var mode_local = 0
          while (true) {
            if (mode_local < 2) {
              var refO = this._readInt1(endRead)
              endRead = refO.endRead
              mode_local++
            } else {
              var find = this._currentObject.data.indexOf(';', endRead)
              if (find === -1) {
                find = this._currentObject.data.length
                mode = 3
                mode_local = 0
              }

              var line = this._currentObject.data.substr(endRead, find - endRead)
              var data = this._readLine(line.trim()).split(',')
              for (let i = 0; i < data.length; i++) {
                this._currentGeo.GeometryData.materialIndices[i] = parseInt(data[i])
              }

              endRead = this._currentObject.data.length
            }

            if (endRead >= this._currentObject.data.length || mode >= 3) {
              break
            }
          }
        },
      },
      {
        key: '_setMaterial',
        value: function _setMaterial() {
          var _nowMat = new MeshPhongMaterial({
            color: Math.random() * 0xffffff,
          })
          _nowMat.side = FrontSide
          _nowMat.name = this._currentObject.name
          var endRead = 0
          var find = this._currentObject.data.indexOf(';;', endRead)
          var line = this._currentObject.data.substr(endRead, find - endRead)
          var data = this._readLine(line.trim()).split(';')
          _nowMat.color.r = parseFloat(data[0])
          _nowMat.color.g = parseFloat(data[1])
          _nowMat.color.b = parseFloat(data[2])
          endRead = find + 2
          find = this._currentObject.data.indexOf(';', endRead)
          line = this._currentObject.data.substr(endRead, find - endRead)
          _nowMat.shininess = parseFloat(this._readLine(line))
          endRead = find + 1
          find = this._currentObject.data.indexOf(';;', endRead)
          line = this._currentObject.data.substr(endRead, find - endRead)
          var data2 = this._readLine(line.trim()).split(';')
          _nowMat.specular.r = parseFloat(data2[0])
          _nowMat.specular.g = parseFloat(data2[1])
          _nowMat.specular.b = parseFloat(data2[2])
          endRead = find + 2
          find = this._currentObject.data.indexOf(';;', endRead)
          if (find === -1) {
            find = this._currentObject.data.length
          }

          line = this._currentObject.data.substr(endRead, find - endRead)
          var data3 = this._readLine(line.trim()).split(';')
          _nowMat.emissive.r = parseFloat(data3[0])
          _nowMat.emissive.g = parseFloat(data3[1])
          _nowMat.emissive.b = parseFloat(data3[2])
          var localObject = null
          while (true) {
            if (this._currentObject.children.length > 0) {
              localObject = this._currentObject.children.shift()
              if (this.debug) {
                console.log('processing ' + localObject.name)
              }

              var fileName = localObject.data.substr(1, localObject.data.length - 2)
              switch (localObject.type) {
                case 'TextureFilename':
                  _nowMat.map = this.texloader.load(fileName)
                  break
                case 'BumpMapFilename':
                  _nowMat.bumpMap = this.texloader.load(fileName)
                  _nowMat.bumpScale = 0.05
                  break
                case 'NormalMapFilename':
                  _nowMat.normalMap = this.texloader.load(fileName)
                  _nowMat.normalScale = new Vector2(2, 2)
                  break
                case 'EmissiveMapFilename':
                  _nowMat.emissiveMap = this.texloader.load(fileName)
                  break
                case 'LightMapFilename':
                  _nowMat.lightMap = this.texloader.load(fileName)
                  break
              }
            } else {
              break
            }
          }

          this._currentGeo.Materials.push(_nowMat)
        },
      },
      {
        key: '_setSkinWeights',
        value: function _setSkinWeights() {
          var boneInf = new XboneInf()
          var endRead = 0
          var find = this._currentObject.data.indexOf(';', endRead)
          var line = this._currentObject.data.substr(endRead, find - endRead)
          endRead = find + 1
          boneInf.boneName = line.substr(1, line.length - 2)
          boneInf.BoneIndex = this._currentGeo.BoneInfs.length
          find = this._currentObject.data.indexOf(';', endRead)
          endRead = find + 1
          find = this._currentObject.data.indexOf(';', endRead)
          line = this._currentObject.data.substr(endRead, find - endRead)
          var data = this._readLine(line.trim()).split(',')
          for (let i = 0; i < data.length; i++) {
            boneInf.Indeces.push(parseInt(data[i]))
          }

          endRead = find + 1
          find = this._currentObject.data.indexOf(';', endRead)
          line = this._currentObject.data.substr(endRead, find - endRead)
          var data2 = this._readLine(line.trim()).split(',')
          for (let _i = 0; _i < data2.length; _i++) {
            boneInf.Weights.push(parseFloat(data2[_i]))
          }

          endRead = find + 1
          find = this._currentObject.data.indexOf(';', endRead)
          if (find <= 0) {
            find = this._currentObject.data.length
          }

          line = this._currentObject.data.substr(endRead, find - endRead)
          var data3 = this._readLine(line.trim()).split(',')
          boneInf.OffsetMatrix = new Matrix4()
          this._ParseMatrixData(boneInf.OffsetMatrix, data3)
          this._currentGeo.BoneInfs.push(boneInf)
        },
      },
      {
        key: '_makePutBoneList',
        value: function _makePutBoneList(_RootName, _bones) {
          var putting = false
          for (let frame in this.HieStack) {
            if (this.HieStack[frame].name === _RootName || putting) {
              putting = true
              var b = new Bone()
              b.name = this.HieStack[frame].name
              b.applyMatrix4(this.HieStack[frame].FrameTransformMatrix)
              b.matrixWorld = b.matrix
              b.FrameTransformMatrix = this.HieStack[frame].FrameTransformMatrix
              b.pos = new Vector3().setFromMatrixPosition(b.FrameTransformMatrix).toArray()
              b.rotq = new Quaternion().setFromRotationMatrix(b.FrameTransformMatrix).toArray()
              b.scl = new Vector3().setFromMatrixScale(b.FrameTransformMatrix).toArray()
              if (this.HieStack[frame].parentName && this.HieStack[frame].parentName.length > 0) {
                for (let i = 0; i < _bones.length; i++) {
                  if (this.HieStack[frame].parentName === _bones[i].name) {
                    _bones[i].add(b)
                    b.parent = i
                    break
                  }
                }
              }

              _bones.push(b)
            }
          }
        },
      },
      {
        key: '_makeOutputGeometry',
        value: function _makeOutputGeometry() {
          var mesh = null
          if (this._currentGeo.BoneInfs.length > 0) {
            var putBones = []
            this._makePutBoneList(this._currentGeo.baseFrame.parentName, putBones)
            for (let bi = 0; bi < this._currentGeo.BoneInfs.length; bi++) {
              var boneIndex = 0
              for (let bb = 0; bb < putBones.length; bb++) {
                if (putBones[bb].name === this._currentGeo.BoneInfs[bi].boneName) {
                  boneIndex = bb
                  putBones[bb].OffsetMatrix = new Matrix4()
                  putBones[bb].OffsetMatrix.copy(this._currentGeo.BoneInfs[bi].OffsetMatrix)
                  break
                }
              }

              for (let vi = 0; vi < this._currentGeo.BoneInfs[bi].Indeces.length; vi++) {
                var nowVertexID = this._currentGeo.BoneInfs[bi].Indeces[vi]
                var nowVal = this._currentGeo.BoneInfs[bi].Weights[vi]

                var stride = nowVertexID * 4

                switch (this._currentGeo.VertexSetedBoneCount[nowVertexID]) {
                  case 0:
                    this._currentGeo.GeometryData.skinIndices[stride] = boneIndex
                    this._currentGeo.GeometryData.skinWeights[stride] = nowVal
                    break
                  case 1:
                    this._currentGeo.GeometryData.skinIndices[stride + 1] = boneIndex
                    this._currentGeo.GeometryData.skinWeights[stride + 1] = nowVal
                    break
                  case 2:
                    this._currentGeo.GeometryData.skinIndices[stride + 2] = boneIndex
                    this._currentGeo.GeometryData.skinWeights[stride + 2] = nowVal
                    break
                  case 3:
                    this._currentGeo.GeometryData.skinIndices[stride + 3] = boneIndex
                    this._currentGeo.GeometryData.skinWeights[stride + 3] = nowVal
                    break
                }

                this._currentGeo.VertexSetedBoneCount[nowVertexID]++
                if (this._currentGeo.VertexSetedBoneCount[nowVertexID] > 4) {
                  console.log('warn! over 4 bone weight! :' + nowVertexID)
                }
              }
            }

            for (let sk = 0; sk < this._currentGeo.Materials.length; sk++) {
              this._currentGeo.Materials[sk].skinning = true
            }

            var offsetList = []
            for (let _bi = 0; _bi < putBones.length; _bi++) {
              if (putBones[_bi].OffsetMatrix) {
                offsetList.push(putBones[_bi].OffsetMatrix)
              } else {
                offsetList.push(new Matrix4())
              }
            }

            var bufferGeometry = this._buildGeometry()
            mesh = new SkinnedMesh(
              bufferGeometry,
              this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[0] : this._currentGeo.Materials,
            )

            this._initSkeleton(mesh, putBones, offsetList)
          } else {
            var _bufferGeometry = this._buildGeometry()
            mesh = new Mesh(
              _bufferGeometry,
              this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[0] : this._currentGeo.Materials,
            )
          }

          mesh.name = this._currentGeo.name
          var worldBaseMx = new Matrix4()
          var currentMxFrame = this._currentGeo.baseFrame.putBone
          if (currentMxFrame && currentMxFrame.parent) {
            while (true) {
              currentMxFrame = currentMxFrame.parent
              if (currentMxFrame) {
                worldBaseMx.multiply(currentMxFrame.FrameTransformMatrix)
              } else {
                break
              }
            }

            mesh.applyMatrix4(worldBaseMx)
          }

          this.Meshes.push(mesh)
        },
      },
      {
        key: '_initSkeleton',
        value: function _initSkeleton(mesh, boneList, boneInverses) {
          var bones = [],
            bone,
            gbone
          var i, il

          for (i = 0, il = boneList.length; i < il; i++) {
            gbone = boneList[i]

            bone = new Bone()
            bones.push(bone)

            bone.name = gbone.name
            bone.position.fromArray(gbone.pos)
            bone.quaternion.fromArray(gbone.rotq)
            if (gbone.scl !== undefined) bone.scale.fromArray(gbone.scl)
          }

          for (i = 0, il = boneList.length; i < il; i++) {
            gbone = boneList[i]

            if (gbone.parent !== -1 && gbone.parent !== null && bones[gbone.parent] !== undefined) {
              bones[gbone.parent].add(bones[i])
            } else {
              mesh.add(bones[i])
            }
          }

          mesh.updateMatrixWorld(true)

          var skeleton = new Skeleton(bones, boneInverses)
          mesh.bind(skeleton, mesh.matrixWorld)
        },
      },
      {
        key: '_readAnimationKey',
        value: function _readAnimationKey() {
          var endRead = 0
          var find = this._currentObject.data.indexOf(';', endRead)
          var line = this._currentObject.data.substr(endRead, find - endRead)
          endRead = find + 1
          var nowKeyType = parseInt(this._readLine(line))
          find = this._currentObject.data.indexOf(';', endRead)
          endRead = find + 1
          line = this._currentObject.data.substr(endRead)
          var data = this._readLine(line.trim()).split(';;,')
          for (let i = 0; i < data.length; i++) {
            var data2 = data[i].split(';')
            var keyInfo = new XKeyFrameInfo()
            keyInfo.type = nowKeyType
            keyInfo.Frame = parseInt(data2[0])
            keyInfo.index = this._currentAnimeFrames.keyFrames.length
            keyInfo.time = keyInfo.Frame
            if (nowKeyType != 4) {
              var frameFound = false
              for (let mm = 0; mm < this._currentAnimeFrames.keyFrames.length; mm++) {
                if (this._currentAnimeFrames.keyFrames[mm].Frame === keyInfo.Frame) {
                  keyInfo = this._currentAnimeFrames.keyFrames[mm]
                  frameFound = true
                  break
                }
              }

              var frameValue = data2[2].split(',')
              switch (nowKeyType) {
                case 0:
                  keyInfo.rot = new Quaternion(
                    parseFloat(frameValue[1]),
                    parseFloat(frameValue[2]),
                    parseFloat(frameValue[3]),
                    parseFloat(frameValue[0]) * -1,
                  )
                  break
                case 1:
                  keyInfo.scl = new Vector3(
                    parseFloat(frameValue[0]),
                    parseFloat(frameValue[1]),
                    parseFloat(frameValue[2]),
                  )
                  break
                case 2:
                  keyInfo.pos = new Vector3(
                    parseFloat(frameValue[0]),
                    parseFloat(frameValue[1]),
                    parseFloat(frameValue[2]),
                  )
                  break
              }

              if (!frameFound) {
                this._currentAnimeFrames.keyFrames.push(keyInfo)
              }
            } else {
              keyInfo.matrix = new Matrix4()
              this._ParseMatrixData(keyInfo.matrix, data2[2].split(','))
              this._currentAnimeFrames.keyFrames.push(keyInfo)
            }
          }
        },
      },
      {
        key: '_makeOutputAnimation',
        value: function _makeOutputAnimation() {
          var animationObj = new XAnimationObj(this.options)
          animationObj.fps = this.animTicksPerSecond
          animationObj.name = this._currentAnime.name
          animationObj.make(this._currentAnime.AnimeFrames)
          this.animations.push(animationObj)
        },
      },
      {
        key: 'assignAnimation',
        value: function assignAnimation(_model, _animation) {
          var model = _model
          var animation = _animation
          if (!model) {
            model = this.Meshes[0]
          }

          if (!animation) {
            animation = this.animations[0]
          }

          if (!model || !animation) {
            return null
          }

          var put = {}
          put.fps = animation.fps
          put.name = animation.name
          put.length = animation.length
          put.hierarchy = []
          for (let b = 0; b < model.skeleton.bones.length; b++) {
            var findAnimation = false
            for (let i = 0; i < animation.hierarchy.length; i++) {
              if (model.skeleton.bones[b].name === animation.hierarchy[i].name) {
                findAnimation = true
                var c_key = animation.hierarchy[i].copy()
                c_key.parent = -1
                if (model.skeleton.bones[b].parent && model.skeleton.bones[b].parent.type === 'Bone') {
                  for (let bb = 0; bb < put.hierarchy.length; bb++) {
                    if (put.hierarchy[bb].name === model.skeleton.bones[b].parent.name) {
                      c_key.parent = bb
                      c_key.parentName = model.skeleton.bones[b].parent.name
                    }
                  }
                }

                put.hierarchy.push(c_key)
                break
              }
            }

            if (!findAnimation) {
              var _c_key = animation.hierarchy[0].copy()
              _c_key.name = model.skeleton.bones[b].name
              _c_key.parent = -1
              for (let k = 0; k < _c_key.keys.length; k++) {
                if (_c_key.keys[k].pos) {
                  _c_key.keys[k].pos.set(0, 0, 0)
                }

                if (_c_key.keys[k].scl) {
                  _c_key.keys[k].scl.set(1, 1, 1)
                }

                if (_c_key.keys[k].rot) {
                  _c_key.keys[k].rot.set(0, 0, 0, 1)
                }
              }

              put.hierarchy.push(_c_key)
            }
          }

          if (!model.geometry.animations) {
            model.geometry.animations = []
          }

          model.geometry.animations.push(AnimationClip.parseAnimation(put, model.skeleton.bones))
          if (!model.animationMixer) {
            model.animationMixer = new AnimationMixer(model)
          }

          return put
        },
      },
      {
        key: '_ParseMatrixData',
        value: function _ParseMatrixData(targetMatrix, data) {
          targetMatrix.set(
            parseFloat(data[0]),
            parseFloat(data[4]),
            parseFloat(data[8]),
            parseFloat(data[12]),
            parseFloat(data[1]),
            parseFloat(data[5]),
            parseFloat(data[9]),
            parseFloat(data[13]),
            parseFloat(data[2]),
            parseFloat(data[6]),
            parseFloat(data[10]),
            parseFloat(data[14]),
            parseFloat(data[3]),
            parseFloat(data[7]),
            parseFloat(data[11]),
            parseFloat(data[15]),
          )
        },
      },
    ])
    return XLoader
  })()

  return XLoader
})()

export { XLoader }
